-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(jupyter): send Jupyter messaging metadata with Deno.jupyter.broadcast
#20714
Conversation
cc: @rgbkrk |
I'm sure others are more familiar with the latest for how to serialize functions / modules to run clientside. Currently I have this hack: // Create an anywidget module (export a "render" function) for the front end
// The `model` is synchronized with `update` messages sent from the comm_msg
let esm = `export ${async function render({ model, el }) {
let { default: confetti } = await import ("https://esm.sh/canvas-confetti@1.6.0");
model.on("change:value", () => confetti({ angle: model.get("value") }));
confetti();
}.toString()}`; But anywidget, just expects you to ship ESM that exports a function called |
metadata
to Deno.jupyter.broadcast
Deno.jupyter.broadcast
Deno.jupyter.broadcast
Deno.jupyter.broadcast
Works with updates to the frontend. Would be awesome to be able to listen for messages from the frontend as well (i.e. enabling 2-way data binding). Not sure that is implemented yet. Screen.Recording.2023-09-27.at.4.58.53.PM.mov |
cli/js/40_jupyter.js
Outdated
} = core.ensureFastOps(); | ||
|
||
globalThis.Deno.jupyter = { | ||
async broadcast(msgType, content, metadata = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for optional metadata
This is awesome, I'm glad you were able to make this fly so quickly. Thank you! |
That's #20592. It would be nice to have the Javascript API for it that we want to expose that is as close to one of the many web standard options (to be Deno-y). You're making me realize we probably need to support the Jupyter message Roughly we're looking at an overall interface of: type DenoJupyter = typeof Deno & {
jupyter: {
broadcast(
msg_type: string,
content: { [key: string]: object },
metadata?: { [key: string]: object },
buffers?: ArrayBuffer[]
): Promise<void>;
};
}; |
Yes! I have some ideas.
Indeed. The other option would be to have a single object for other possible message data. Not sure if the other fields could be something we'd expose in the future. type DenoJupyter = typeof Deno & {
jupyter: {
broadcast(
msg_type: string,
content: { [key: string]: object },
extra?: {
metadata?: Record<string, object>,
buffers?: ArrayBuffer[]
}
): Promise<void>;
};
}; |
This seems like a reasonable solution to avoid balooning number of arguments this API accepts. |
Famous last words and all, the jupyter spec will not extend the body of the message beyond what is there with parent_header, header, content, metadata, and buffers. I'm totally cool with |
88fea55
to
6004cb2
Compare
6004cb2
to
675603a
Compare
Done, @bartlomieju! (TIL
Okay, how about we introduce It's nice to have the explicit names (as well as the option to extend Deno.jupyter.broadcast(
"comm_open",
{
"comm_id": "blah",
"data": {
"method": "update",
"state": { "value": null },
"buffer_paths": [["value"]]
}
},
{
metadata: { "version": "2.1.0" },
buffers: [new Uint8Array([1, 2, 3, 4, 5]).buffer]
}
); I doubt the API would explode with the positional arguments, but this way the ordering of arguments isn't important. Since Deno.jupyter.broadcast(msgType, content, undefined, buffers); vs Deno.jupyter.broadcast(msgType, content, { buffers }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, could you please update cli/tsc/dts/lib.deno.unstable.d.ts
to match the new JS API?
done! |
Implemented a import { widget } from "https://raw.githubusercontent.com/manzt/anywidget/main/packages/anywidget/mod.ts";
let model = await widget({
// shared model between frontend and backend
state: { value: 0 },
// front-end render function
render: ({ model, el }) => {
let h1 = Object.assign(document.createElement("h1"), {
innerText: "Value is " + model.get("value"),
});
model.on("change:value", () => {
h1.innerText = "Value is " + model.get("value");
});
el.appendChild(h1);
}
});
model for (let i = 1; i <= 10; i++) {
model.set("value", i);
await new Promise(resolve => setTimeout(resolve, 500));
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you @manzt, great improvement and I can't wait to see what you'll be able to do with it!
Wrote a way to do imports with the anywidget in manzt/anywidget#311 |
…adcast` (#20714) Exposes [`metadata`](https://jupyter-client.readthedocs.io/en/latest/messaging.html#metadata) to the `Deno.jupyter.broadcast` API. ```js await Deno.jupyter.broadcast(msgType, content, metadata); ``` The metadata is required for [`"comm_open"`](https://github.com/jupyter-widgets/ipywidgets/blob/main/packages/schema/messages.md#instantiating-a-widget-object-1) for with `jupyter.widget` target.
Will rebase following merge of #20710 in to
main
.Exposes
metadata
to theDeno.jupyter.broadcast
API.The metadata is required for
"comm_open"
for withjupyter.widget
target. With this PR I can successfully communicate with theanywidget
frontend, sending frontend ESM via comms with updates :)Example
Make sure you have anywidget installed:
This is sooo cool!!
Screen.Recording.2023-09-27.at.2.44.30.PM.mov